#include "TextRenderer.h"

TextRenderer::TextRenderer(Shader& shader, const GLuint windowWidth, const GLuint windowHeight)
    : shader(shader), windowWidth(windowWidth), windowHeight(windowHeight), squareSize(windowHeight/18.0f)
{
    this->initRenderData();
}

TextRenderer::~TextRenderer() {
    glDeleteVertexArrays(1, &this->quadVAO);
}

void TextRenderer::initRenderData() {
    // FreeType
    FT_Library ft;
    // All functions return a value different than 0 whenever an error occurred
    if (FT_Init_FreeType(&ft))
        std::cout << "ERROR::FREETYPE: Could not init FreeType Library" << std::endl;

    // Load font as face
    FT_Face face;
    if (FT_New_Face(ft, "font/emulogic.ttf", 0, &face))
        std::cout << "ERROR::FREETYPE: Failed to load font" << std::endl;

    // Set size to load glyphs as
    FT_Set_Pixel_Sizes(face, 0, squareSize);

    // Disable byte-alignment restriction
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    // Load first 128 characters of ASCII set
    for (GLubyte c = 0; c < 128; c++)
    {
        // Load character glyph
        if (FT_Load_Char(face, c, FT_LOAD_RENDER))
        {
            std::cout << "ERROR::FREETYTPE: Failed to load Glyph" << std::endl;
            continue;
        }

        // Generate texture
        Texture texture(GL_RED, GL_RED, GL_CLAMP_TO_BORDER, GL_CLAMP_TO_BORDER, GL_LINEAR, GL_LINEAR);
        texture.generate(face->glyph->bitmap.width, face->glyph->bitmap.rows, face->glyph->bitmap.buffer);

        // Now store character for later use
        Character character = {
            texture,
            glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows),
            glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top),
            face->glyph->advance.x
        };
        characters.insert(std::pair<GLchar, Character>(c, character));
    }
    glBindTexture(GL_TEXTURE_2D, 0);
    // Destroy FreeType once we're finished
    FT_Done_Face(face);
    FT_Done_FreeType(ft);

    // Configure VAO/VBO for texture quads
    glGenVertexArrays(1, &quadVAO);
    glGenBuffers(1, &quadVBO);
    glBindVertexArray(quadVAO);
    glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 5, NULL, GL_DYNAMIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(0));
    glEnableVertexAttribArray(0);   // position

    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);   // texCoords

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
}

void TextRenderer::renderText(std::string text, glm::vec2 position, GLfloat scale, glm::vec3 color) {
    // Activate corresponding render state
    shader.use();
    shader.setVector3f("textColor", color);
    glActiveTexture(GL_TEXTURE0);
    glBindVertexArray(this->quadVAO);

    GLfloat x = position.x * squareSize;
    GLfloat y = position.y * squareSize;
    // Iterate through all characters
    std::string::const_iterator c;
    for (c = text.begin(); c != text.end(); c++) {
        Character ch = characters[*c];

        GLfloat xpos = x + ch.Bearing.x * scale;
        GLfloat ypos = y + (this->characters['H'].Bearing.y - ch.Bearing.y) * scale;

        GLfloat w = ch.Size.x * scale;
        GLfloat h = ch.Size.y * scale;
        // Update VBO for each character
        GLfloat vertices[6][5] = {
            { xpos,     ypos + h,   1.0, 0.0, 1.0 },
            { xpos + w, ypos,       1.0, 1.0, 0.0 },
            { xpos,     ypos,       1.0, 0.0, 0.0 },

            { xpos,     ypos + h,   1.0, 0.0, 1.0 },
            { xpos + w, ypos + h,   1.0, 1.0, 1.0 },
            { xpos + w, ypos,       1.0, 1.0, 0.0 }
        };
        // Render glyph texture over quad
        ch.texture.bind();

        // Update content of VBO memory
        glBindBuffer(GL_ARRAY_BUFFER, this->quadVBO);
        glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); // Be sure to use glBufferSubData and not glBufferData

        glBindBuffer(GL_ARRAY_BUFFER, 0);
        // Render quad
        glDrawArrays(GL_TRIANGLES, 0, 6);
        // Now advance cursors for next glyph (note that advance is number of 1/64 pixels)
        x += (ch.Advance >> 6) * scale; // Bitshift by 6 to get value in pixels (2^6 = 64 (divide amount of 1/64th pixels by 64 to get amount of pixels))
    }
    glBindVertexArray(0);
    glBindTexture(GL_TEXTURE_2D, 0);
}

void TextRenderer::renderText(std::string text, glm::vec3 position, GLfloat scale, glm::vec3 color) {
    // Activate corresponding render state
    shader.use();
    shader.setVector3f("textColor", color);
    glActiveTexture(GL_TEXTURE0);
    glBindVertexArray(this->quadVAO);

    GLfloat x = position.x * squareSize;
    GLfloat y = position.y * squareSize;
    GLfloat z = position.z * squareSize;
    // Iterate through all characters
    std::string::const_iterator c;
    for (c = text.begin(); c != text.end(); c++) {
        Character ch = characters[*c];

        GLfloat xpos = x + ch.Bearing.x * scale;
        GLfloat zpos = z + (this->characters['H'].Bearing.y - ch.Bearing.y) * scale;

        GLfloat w = ch.Size.x * scale;
        GLfloat h = ch.Size.y * scale;
        // Update VBO for each character
        GLfloat vertices[6][5] = {
            { xpos,     y, zpos + h,   0.0, 1.0 },
            { xpos + w, y, zpos,       1.0, 0.0 },
            { xpos,     y, zpos,       0.0, 0.0 },

            { xpos,     y, zpos + h,   0.0, 1.0 },
            { xpos + w, y, zpos + h,   1.0, 1.0 },
            { xpos + w, y, zpos,       1.0, 0.0 }
        };
        // Render glyph texture over quad
        ch.texture.bind();

        // Update content of VBO memory
        glBindBuffer(GL_ARRAY_BUFFER, this->quadVBO);
        glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); // Be sure to use glBufferSubData and not glBufferData

        glBindBuffer(GL_ARRAY_BUFFER, 0);
        // Render quad
        glDrawArrays(GL_TRIANGLES, 0, 6);
        // Now advance cursors for next glyph (note that advance is number of 1/64 pixels)
        x += (ch.Advance >> 6) * scale; // Bitshift by 6 to get value in pixels (2^6 = 64 (divide amount of 1/64th pixels by 64 to get amount of pixels))
    }
    glBindVertexArray(0);
    glBindTexture(GL_TEXTURE_2D, 0);
}
